home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / www / src / WWW / Daemon / Implementation / HTDaemon.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-29  |  28.3 KB  |  1,075 lines

  1. /*        TCP/IP based server for HyperText        HTDaemon.c
  2. **        ---------------------------------
  3. **
  4. **
  5. ** Compilation options:
  6. **    RULES        If defined, use rule file and translation table
  7. **    DIR_OPTIONS    If defined, -d options control directory access
  8. **
  9. **  Authors:
  10. **    TBL    Tim Berners-Lee, CERN
  11. **    JFG    Jean-Francois Groff, CERN
  12. **    JS    Jonathan Streets, FNAL
  13. **
  14. **  History:
  15. **       Sep 91  TBL     Made from earlier daemon files. (TBL)
  16. **    26 Feb 92  JFG    Bug fixes for Multinet.
  17. **       8 Jun 92  TBL  Bug fix: Perform multiple reads in case we don't get
  18. **                      the whole command first read.
  19. **    25 Jun 92  JFG  Added DECNET option through TCP socket emulation.
  20. **     6 Jan 93  TBL  Plusses turned to spaces between keywords
  21. **     7 Jan 93  JS   Bug fix: addrlen had not been set for accept() call
  22. **            Logging in GMT to file-YYMM in name
  23. */
  24. /* (c) CERN WorldWideWeb project 1990-1992. See Copyright.html for details */
  25.  
  26.  
  27. /*    Module parameters:
  28. **    -----------------
  29. **
  30. **  These may be undefined and redefined by syspec.h
  31. */
  32.  
  33. #define LISTEN_BACKLOG 2    /* Number of pending connect requests (TCP)*/
  34. #define MAX_CHANNELS 20        /* Number of channels we will keep open */
  35. #define WILDCARD '*'            /* Wildcard used in addressing */
  36. #define FIRST_TCP_PORT    5000    /* When using dynamic allocation */
  37. #define LAST_TCP_PORT 5999
  38.  
  39. #define MAX_LINE 512        /* HTTP request field line */
  40.  
  41. #ifndef RULE_FILE
  42. #define RULE_FILE        "/etc/httpd.conf"
  43. #endif
  44.  
  45. #ifndef DEFAULT_EXPORT
  46. #define DEFAULT_EXPORT        "/Public"
  47. #endif
  48.  
  49. #include "HTUtils.h"
  50. #include "tcp.h"        /* The whole mess of include files */
  51. #include "HTTCP.h"        /* Some utilities for TCP */
  52. #include "HTFormat.h"
  53. #include "HTInit.h"
  54.  
  55. #ifdef RULES            /* Use rules? */
  56. #include "HTRules.h"
  57. #endif
  58.  
  59. #include "HTFile.h"
  60. #include "HTParse.h"
  61.  
  62. extern int HTRetrieve PARAMS((char * arg, int soc));    /* Handle one request */
  63.  
  64.  
  65.  
  66. /*    Module-Global Variables
  67. **    -----------------------
  68. */
  69. PRIVATE enum role_enum {master, slave, transient, passive} role;
  70. PRIVATE SockA    soc_address;
  71. PRIVATE int    soc_addrlen;
  72. PRIVATE int    master_soc;    /* inet socket number to listen on */
  73. PRIVATE int    com_soc;    /* inet socket number to read on */
  74. #ifdef SELECT
  75. PRIVATE fd_set    open_sockets;    /* Mask of channels which are active */
  76. PRIVATE int    num_sockets;    /* Number of sockets to scan */
  77. #endif
  78. PRIVATE BOOLEAN    dynamic_allocation;    /* Search for port? */
  79.  
  80. PRIVATE time_t theTime;     /* A long integer giving the datetime in secs */
  81. PRIVATE struct tm * gmt;    /* The time in GMT broken down */
  82.  
  83. /*    Program-Global Variables
  84. **    ------------------------
  85. */
  86. PUBLIC char * HTAppName = "CERN-HTTPD";    /* Application name */
  87. PUBLIC char * HTAppVersion = VD;     /* Application version */
  88.  
  89.  
  90. /* PUBLIC int    WWW_TraceFlag;    in libwww/HTString.c  Control flag for diagnostics */
  91.  
  92. PUBLIC char * log_file_name = 0;/* Log file name if any (WAIS code) */
  93.  
  94. PUBLIC char * HTClientProtocol = 0;    /* Protocol and version number */
  95. PUBLIC char * HTServerProtocol = "HTTP/1.0";
  96.  
  97. extern char * HTClientHost;    /* in library or HTRetrieve */
  98.  
  99. PRIVATE FILE * serverlog; /* Log file if any -- don't set up public one */
  100.  
  101. #define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r'))     /*  DMX */
  102.  
  103.  
  104.  
  105. /*    Split fields
  106. **    ------------
  107. **
  108. ** On entry,
  109. **    s    points to string with words or quoted strings as fields
  110. **        separated by white space
  111. ** On exit,
  112. **    Return value points to first char of second word or NULL if none.
  113. **    First word is null-terminated.
  114. **    All trailing white space is overwritten with zero.
  115. */
  116. PRIVATE char * next_field ARGS1(char *, s)
  117. {
  118.     while(*s && SPACE(*s))s++;    /* skip leading blanks */
  119.     switch(*s) {
  120.         case '"':
  121.     case '\'':
  122.         s = strchr(s, *s);    /* skip quoted word */
  123.         if (!s) return s;    /* No closing quote! */
  124.         s++;            /* Skip closing quote */
  125.         break;
  126.     default:
  127.         while(*s && !SPACE(*s)) s++;    /* skip word */
  128.     }
  129.     if (!*s) return (char*)0;    /* Only one  or no word */
  130.     
  131.     *s++ = (char)0;        /* terminate first word */
  132.     while(SPACE(*s))s++;    /* skip leading blanks */
  133.     if (!*s) return (char*)0;    /* No second word */
  134.     return s;
  135. }
  136.  
  137.  
  138. /*    Send a string down a socket
  139. **    ---------------------------
  140. **
  141. **    The trailing zero is not sent.
  142. **    There is a maximum string length.
  143. */
  144. PUBLIC int HTWriteASCII ARGS2(int, soc, char *, s)
  145. {
  146. #ifdef NOT_ASCII
  147.     char * p;
  148.     char ascii[255];
  149.     char *q = ascii;
  150.     for (p=s; *p; p++) {
  151.         *q++ = TOASCII(*p);
  152.     }
  153.     return NETWRITE(soc, ascii, p-s);
  154. #else
  155.     return NETWRITE(soc, s, strlen(s));
  156. #endif
  157. }
  158.  
  159.  
  160.  
  161. /*                MIME OUTPUT
  162. **                ===========
  163. */
  164.  
  165. struct _HTStream {
  166.     HTStreamClass *        isa;
  167.     HTStreamClass        class;        /* special per object */
  168. };
  169.  
  170. /*    Write header for given content type
  171. **    -----------------------------------
  172. **
  173. **
  174. ** Note:  Under old protocol, non-plaintext files are sent untouched
  175. **    to work with multimedia hack in XMosaic.
  176. */
  177. #ifdef NOT_NEEDED_IT_SEEMS
  178. PUBLIC void HTSendHeader ARGS2(
  179.     int,                 soc, 
  180.     HTFormat,            rep)
  181. {
  182.     if (TRACE) fprintf(stderr, "HTDaemon: Retrieve Ok, Content-type %s\n",
  183.             HTAtom_name(rep));
  184.     if (HTClientProtocol) {
  185.     char buffer[4096];    /* @@ */
  186.     sprintf(buffer, "%s%s%s%s%s",
  187.         HTServerProtocol,
  188.           " 200 Document follows\r\nMIME-Version: 1.0\r\n",
  189.         "Content-Type: ", HTAtom_name(rep), "\r\n\r\n");
  190.     HTWriteASCII(soc, buffer);
  191.     
  192.     } else {            /* Old protocol */
  193.         if (rep == WWW_PLAINTEXT) {
  194.         HTWriteASCII(soc, "<PLAINTEXT>\r\n");
  195.     }
  196.     }
  197. }
  198. #endif
  199.  
  200. /*            Catch error messages
  201. **            --------------------
  202. **
  203. **    This shouldn't be necessary most of the time as HTLoadError
  204. **    will be called.
  205. **
  206. **    These entry points suppress the loading of HTAlert from the WWW library.
  207. **    These could be cleaned up and made very useful, esp
  208. **    remote progress reporting...
  209. */
  210.  
  211. PUBLIC void HTAlert ARGS1(CONST char *, Msg)
  212. {
  213.     fprintf(stderr, "500   Server reports:  %s\r\n", Msg);
  214. }
  215.  
  216.  
  217. PUBLIC void HTProgress ARGS1(CONST char *, Msg)
  218. {
  219.     /* fprintf(stderr, "   %s ...\n", Msg); */
  220. }
  221.  
  222.  
  223. PUBLIC BOOL HTConfirm ARGS1(CONST char *, Msg)
  224. {
  225.     return(NO);
  226. }
  227.  
  228. /*    Prompt for answer and get text back
  229. */
  230. PUBLIC char * HTPrompt ARGS2(CONST char *, Msg, CONST char *, deflt)
  231. {
  232.     char * rep = 0;
  233.     StrAllocCopy(rep, deflt);
  234.     return rep;
  235. }
  236.  
  237. /*    Write Error Message
  238. **    -------------------
  239. **
  240. **
  241. */
  242. PUBLIC int HTLoadError ARGS3(
  243.     HTStream*,             sink, 
  244.     int,                number,
  245.     CONST char *,            message)
  246. {
  247.     char buffer[4096];    /* @@ */
  248.     if (TRACE) fprintf(stderr, "HTDaemon: *** Returning ERROR %d:\n   %s\n",
  249.             number, message);
  250.     if (HTClientProtocol) {
  251.     sprintf(buffer,
  252.     "%s %d %s\r\nMIME-Version: 1.0\r\nContent-Type: text/HTML\r\n\r\n",
  253.         HTServerProtocol,
  254.         number,
  255.         message);
  256.     (*sink->isa->put_string)(sink, buffer);
  257.     }
  258.     sprintf(buffer,
  259.      "<BODY><H1>Error %d</H1>\r\n\r\n  %s</BODY>\r\n",
  260.         number,
  261.         message);
  262.     (*sink->isa->put_string)(sink, buffer);
  263.     (*sink->isa->end_document)(sink);
  264.     (*sink->isa->free)(sink);
  265.     return HT_LOADED;
  266. }
  267.  
  268.  
  269. /*    Converter for writing the MIME wrapper to a document
  270. **    ----------------------------------------------------
  271. **
  272. **    Thsi kinda cheats because it returns the sink stream
  273. **    having first sent the wrapper down it.  Efficient -- I like it
  274. **    better than the MIME parser which gets in the way from then on.
  275. */
  276. PUBLIC HTStream * HTMIMEWrapper ARGS3(
  277.             HTPresentation *,    pres,
  278.             HTParentAnchor *,    anchcor,
  279.             HTStream*,        sink)
  280. {
  281.  
  282.     if (TRACE) fprintf(stderr, "HTDaemon: Retrieve Ok, Content-type %s\n",
  283.             HTAtom_name(pres->rep));
  284.     if (HTClientProtocol) {
  285.     char buffer[4096];    /* @@ */
  286.     sprintf(buffer, "%s%s%s%s%s",
  287.         HTServerProtocol,
  288.           " 200 Document follows\r\nMIME-Version: 1.0\r\n",
  289.         "Content-Type: ", HTAtom_name(pres->rep), "\r\n\r\n");
  290.     (*sink->isa->put_string)(sink, buffer);
  291.     
  292.     } else {            /* Old protocol */
  293.         if (pres->rep == WWW_PLAINTEXT) {
  294.         (*sink->isa->put_string)(sink, "<PLAINTEXT>\r\n");
  295.     }
  296.     }
  297.  
  298.     return sink;
  299. }
  300.  
  301.  
  302. /*____________________________________________________________________
  303. **
  304. **            Networking code
  305. */
  306.  
  307. /*        Bind to a TCP port
  308. **        ------------------
  309. **
  310. ** On entry,
  311. **    tsap    is a string explaining where to take data from.
  312. **        ""     means data is taken from stdin.
  313. **        "*:1729" means "listen to anyone on port 1729"
  314. **
  315. ** On exit,
  316. **    returns        Negative value if error.
  317. */
  318. int do_bind ARGS1(CONST char *, tsap)
  319. {
  320. #ifdef SELECT
  321.     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
  322.     num_sockets = 0;
  323. #endif
  324.  
  325. /*  Deal with PASSIVE socket:
  326. **
  327. **    A passive TSAP is one which has been created by the inet daemon.
  328. **    It is indicated by a void TSAP name.  In this case, the inet
  329. **    daemon has started this process and given it, as stdin, the connection
  330. **    which it is to use.
  331. */
  332.     if (*tsap == 0) {            /* void tsap => passive */
  333.  
  334.     dynamic_allocation = FALSE;        /* not dynamically allocated */
  335.     role = passive;    /* Passive: started by daemon */
  336.  
  337. #ifdef vms
  338.  
  339.     {   unsigned short channel;        /* VMS I/O channel */
  340.         struct string_descriptor {        /* This is NOT a proper descriptor*/
  341.             int size;            /*  but it will work.          */
  342.             char *ptr;            /* Should be word,byte,byte,long  */
  343.         } sys_input = {10, "SYS$INPUT:"};
  344.         int    status;            /* Returned status of assign */
  345.         extern int sys$assign();
  346.  
  347.         status = sys$assign(&sys_input, &channel, 0, 0);
  348.         com_soc = channel;    /* The channel is stdin */
  349.         CTRACE(tfp, "IP: Opened PASSIVE socket %d\n", channel);
  350.         return 1 - (status&1);
  351.     }    
  352. #else
  353.     com_soc = 0;        /* The channel is stdin */
  354.     CTRACE(tfp, "IP: PASSIVE socket 0 assumed from inet daemon\n");
  355.     return 0;        /* Good */
  356. #endif
  357.  
  358. /*  Parse the name (if not PASSIVE)
  359. */
  360.     } else {                /* Non-void TSAP */
  361.     char *p;        /* pointer to string */
  362.     char *q;
  363.     struct hostent  *phost;        /* Pointer to host - See netdb.h */
  364.     char buffer[256];        /* One we can play with */
  365.     register SockA * sin = &soc_address;
  366.  
  367.     strcpy(buffer, tsap);
  368.     p = buffer;
  369.  
  370. /*  Set up defaults:
  371. */
  372. #ifdef DECNET
  373.     sin->sdn_family = AF_DECnet;        /* Family = DECnet, host order  */
  374.     sin->sdn_objnum = 0;                /* Default: new object number, */
  375. #else  /* Internet */
  376.     sin->sin_family = AF_INET;        /* Family = internet, host order  */
  377.     sin->sin_port = 0;            /* Default: new port,    */
  378. #endif
  379.     dynamic_allocation = TRUE;        /*  dynamically allocated */
  380.     role = passive;             /*  by default */
  381.  
  382. /*  Check for special characters:
  383. */
  384.     if (*p == WILDCARD) {        /* Any node */
  385.         role = master;
  386.         p++;
  387.     }
  388.  
  389. /*  Strip off trailing port number if any:
  390. */
  391.     for(q=p; *q; q++)
  392.         if (*q==':') {
  393.             int status = 0;
  394.         *q++ = 0;        /* Terminate node string */
  395. #ifdef DECNET
  396.         sin->sdn_objnum = (unsigned char) HTCardinal(
  397.                         &status, &q, (unsigned int)65535);
  398. #else
  399.         sin->sin_port = htons((unsigned short)HTCardinal(
  400.                         &status, &q, (unsigned int)65535));
  401.         if (status<0) return status;
  402. #endif
  403.         if (*q) return -2;  /* Junk follows port number */
  404.         dynamic_allocation = FALSE;
  405.         break;        /* Exit from loop before we skip the zero */
  406.         } /*if*/
  407.  
  408. /* Get node name:
  409. */
  410. #ifdef DECNET  /* Empty address (don't care about the command) */
  411.     sin->sdn_add.a_addr[0] = 0;
  412.     sin->sdn_add.a_addr[1] = 0;
  413.     CTRACE(tfp, 
  414.         "Daemon: Parsed address as port %d, DECnet %d.%d\n",
  415.             (int) sin->sdn_objnum,
  416.             (int) sin->sdn_add.a_addr[0],
  417.             (int) sin->sdn_add.a_addr[1] ) ;
  418. #else
  419.     if (*p == 0) {
  420.         sin->sin_addr.s_addr = INADDR_ANY; /* Default: any address */
  421.  
  422.     } else if (*p>='0' && *p<='9') {   /* Numeric node address: */
  423.         sin->sin_addr.s_addr = inet_addr(p); /* See arpa/inet.h */
  424.  
  425.     } else {            /* Alphanumeric node name: */
  426.         phost=gethostbyname(p);    /* See netdb.h */
  427.         if (!phost) {
  428.         CTRACE(tfp, "IP: Can't find internet node name `%s'.\n",p);
  429.         return HTInetStatus("gethostbyname");  /* Fail? */
  430.         }
  431.         memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
  432.     }
  433.     CTRACE(tfp, 
  434.         "Daemon: Parsed address as port %d, inet %d.%d.%d.%d\n",
  435.             (int)ntohs(sin->sin_port),
  436.             (int)*((unsigned char *)(&sin->sin_addr)+0),
  437.             (int)*((unsigned char *)(&sin->sin_addr)+1),
  438.             (int)*((unsigned char *)(&sin->sin_addr)+2),
  439.             (int)*((unsigned char *)(&sin->sin_addr)+3));
  440. #endif
  441.     } /* scope of p */
  442.  
  443.  
  444. /*  Master socket for server:
  445. */
  446.     if (role == master) {
  447.  
  448. /*  Create internet socket
  449. */
  450. #ifdef DECNET
  451.     master_soc = socket(AF_DECnet, SOCK_STREAM, 0);
  452. #else
  453.     master_soc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  454. #endif
  455.     if (master_soc<0)
  456.         return HTInetStatus("socket");
  457.       
  458.     CTRACE(tfp, "IP: Opened socket number %d\n", master_soc);
  459.     
  460. /*  If the port number was not specified, then we search for a free one.
  461. */
  462. #ifndef DECNET  /* irrelevant: no inetd */
  463.     if (dynamic_allocation) {
  464.         unsigned short try;
  465.         for (try=FIRST_TCP_PORT; try<=LAST_TCP_PORT; try++) { 
  466.         soc_address.sin_port = htons(try);
  467.         if (bind(master_soc,
  468.             (struct sockaddr*)&soc_address,
  469.                 /* Cast to generic sockaddr */
  470.             sizeof(soc_address)) == 0)
  471.             break;
  472.         if (try == LAST_TCP_PORT)
  473.             return HTInetStatus("bind");
  474.         }
  475.         CTRACE(tfp, "IP:  Bound to port %d.\n",
  476.             ntohs(soc_address.sin_port));
  477.     } else
  478. #endif
  479.       {                    /* Port was specified */
  480.         if (bind(master_soc,
  481.              (struct sockaddr*)&soc_address,    /* Cast to generic address */
  482.              sizeof(soc_address))<0)
  483.         return HTInetStatus("bind");
  484.     }
  485.     if (listen(master_soc, LISTEN_BACKLOG)<0)
  486.         return HTInetStatus("listen");
  487.  
  488.     CTRACE(tfp, "Daemon: Master socket(), bind() and listen() all OK\n");
  489. #ifdef SELECT
  490.     FD_SET(master_soc, &open_sockets);
  491.     if ((master_soc+1) > num_sockets) num_sockets=master_soc+1;
  492. #endif
  493.     return master_soc;
  494.     } /* if master */
  495.     
  496.     return -1;        /* unimplemented role */
  497.  
  498. } /* do_bind */
  499.  
  500.  
  501. /*    Read One Line
  502. **    -------------
  503. **
  504. ** On exit,
  505. **    returns     A malloced buffer with the line in.
  506. **            Rest of buffer is intact in command
  507. **            not converted into ASCII in case it is binary.
  508. */
  509. #define COMMAND_SIZE     2048   /* @@@@ WAIS queries can be big! */
  510. #define MINIMUM_READ      256   /* Minimum  left before we reallocate */
  511. #define ALLOCATION_UNIT  2048   /* Amount extra we add on each time */
  512.  
  513. PRIVATE int allocated = 0;
  514. PRIVATE int write_pointer = 0;
  515. PRIVATE int read_pointer = 0;
  516. PRIVATE char * command = NULL;
  517.  
  518. PRIVATE char * get_line ARGS1(
  519.     int,        soc)
  520.  
  521. {
  522.   char * line;
  523.   int status;
  524.   if (!command) {        /* First allocation */
  525.     allocated = COMMAND_SIZE;
  526.     write_pointer = 0;        /* valid data in buffer */
  527.     read_pointer = 0;
  528.     command = (char *)malloc(allocated+1);
  529.     *command = 0;      /* terminate "left-over" */
  530.     if (!command) {
  531.       fprintf(stderr, "Daemon: insufficient memory!\n");
  532.       exit(-5);
  533.     }
  534.   }
  535.  
  536.   for(;;) {    /* Get more if needed to complete line */
  537.     if (read_pointer == write_pointer) {    /* Need more data */
  538.       if (allocated - write_pointer < MINIMUM_READ) {
  539.     allocated = allocated + ALLOCATION_UNIT;
  540.     command = (char *)realloc(command, allocated+1);
  541.     if (!command) {
  542.       fprintf(stderr, "Daemon: No memory to reallocate command buffer!!\n");
  543.       exit(-6);
  544.     }
  545.       }
  546.       status = NETREAD(soc, command + write_pointer, allocated - write_pointer);
  547.       if (TRACE) fprintf(stderr,
  548.         "Daemon: net read returned %d, errno=%d\n", status, errno);
  549.       if (status<=0) {
  550.     free(command);
  551.     return NULL;    /* EOF or error before NL  */
  552.       }
  553.       write_pointer = write_pointer + status;
  554.       command[write_pointer] = 0;    /* terminate new string */
  555.     }
  556.  
  557.     /*    Find a line feed if there is one */
  558.     
  559.     for(; read_pointer < write_pointer; read_pointer++) {
  560.         char c;
  561. #ifdef NOT_ASCII
  562.     command[read_pointer] = TOASCII(command[read_pointer]);
  563. #endif
  564.     c = command[read_pointer];
  565.     if (!c) {
  566.       free(command);
  567.       return NULL;   /* Panic! read a 0! */
  568.     }
  569.     if (c=='\n') {   /* found a line feed: split buffers*/
  570.       line = command;
  571.       command[read_pointer++] = 0;     /* terminate and split lines */
  572.       write_pointer = write_pointer - read_pointer;
  573.       command = (char *)malloc(allocated+1);
  574.       if (!command) {
  575.         fprintf(stderr, "Daemon: insufficient memory for line!\n");
  576.         exit(-7);
  577.       }
  578.       memcpy(command, &line[read_pointer], write_pointer);  
  579.       read_pointer = 0;
  580.       return line;
  581.     }
  582.     
  583.     }  /* scan over buffer */
  584.   } /* end loop getting and scanning data */
  585. }
  586.  
  587.  
  588. /*    Handle one message
  589. **    ------------------
  590. **
  591. ** On entry,
  592. **    soc        A file descriptor for input and output.
  593. ** On exit,
  594. **    returns        >0    Channel is still open.
  595. **            0    End of file was found, please close file
  596. **            <0    Error found, please close file.
  597. */
  598. PUBLIC int HTHandle ARGS1(int, soc)
  599. {
  600.  
  601.   char *line1;         /* To hold command read from client */
  602.   char *keywords;    /* pointer to keywords in address */
  603.   int    status;
  604.   char * arg;        /* Pointer to the argument string */
  605.     
  606.        
  607.   if (command) {
  608.     free(command);
  609.     command = 0;
  610.   }
  611.  
  612.   line1 = get_line(soc);   /* Free me later */
  613.  
  614. /*    Log the call:
  615. */
  616.   if (serverlog) {
  617.     fprintf(serverlog, "%24.24s %s %s\n",
  618.           asctime(gmt), HTClientHost, line1);
  619.     fflush(serverlog);    /* Actually update it on disk */
  620.     if (TRACE) fprintf(stderr, "Log: %24.24s %s %s",
  621.               asctime(gmt), HTClientHost, line1);
  622.   }
  623.  
  624.   arg=next_field(line1);
  625.         
  626. /*    @@@@@@@@@ Clear out any conversions to "present" left from before !!
  627. **    for when running in a loop
  628. */
  629.     HTFormatInit();    /* set up the list */
  630.  
  631. #ifndef NOCONVERT
  632.   HTSetConversion("text/plain", "www/present", HTMIMEWrapper,
  633.           1.0, 0.0, 0.0);
  634.   HTSetConversion("text/html", "www/present", HTMIMEWrapper,
  635.           1.0, 0.0, 0.0);
  636.   HTSetConversion("application/binary", "www/present", HTMIMEWrapper,
  637.           0.7, 0.0, 0.0);    /* Risky & more trouble */
  638. #endif            
  639.   if (arg) {
  640.  
  641.     HTClientProtocol = next_field(arg);
  642.     
  643.     if (HTClientProtocol) {
  644.       
  645.       char *line;     /* free me! @@@@@ */
  646.       char *q;
  647.       enum _request_field { INVALID, ACCEPT } field = INVALID;
  648.  
  649.       (void) next_field(HTClientProtocol); /* Strip trailing space */
  650.       while((line=get_line(soc)) != 0) {
  651.     char * p;
  652.     if (!*line) break;        /* Just LF */
  653.     p = line + strlen(line) - 1;
  654.     if (*p == '\r') *p = 0;
  655.     if (!*line) break;        /* Just CRLF */
  656.     if (!WHITE(*line)) {            /* has field */
  657.       p = strchr(line, ':');
  658.       if (!p) break;        /* Bad format -- junk line */
  659.       field = !strncasecmp(line, "Accept:", 7) ? ACCEPT
  660.         : INVALID;
  661.           p++;     /* skip colon */
  662.     } else {                        /* continuation line */
  663.       p = line;
  664.     }
  665.     if (field != INVALID) { /* Look for good one */
  666.       float quality = 1.0;
  667.       float maxbytes = 0.0;
  668.       float maxsecs = 0.0;
  669.     
  670.       q = next_field(p);
  671.       p = HTStrip(p);    /* Strip leading and trailing */
  672.     
  673.       while (q) {        /* more data left */
  674.         float value;
  675.         char * next;
  676.         char *equals = strchr(q, '=');
  677.         if (!equals) break;    /* bad syntax -- forget it! */
  678.         *equals++ = 0;    /* Split at equals */
  679.         next = next_field(equals);
  680.         if (sscanf(equals, "%f", &value)) {
  681.           char * attrib = HTStrip(q);
  682.           if (!strcasecmp(attrib, "q")) quality = value;
  683.           else if (!strcasecmp(attrib, "mxb")) maxbytes = value;
  684.           else if (!strcasecmp(attrib, "mxs")) maxsecs = value;
  685.         }
  686.         q = next;
  687.       } /* scan attributes */
  688.       
  689.       if (field == ACCEPT) {
  690.         if(TRACE) fprintf(stderr, "Daemon: Client accepts %s\n", p);
  691. #ifndef NOCONVERT
  692.         HTSetConversion(p, "www/present", HTMIMEWrapper,
  693.                 quality, 0.0, 0.0);    /* @@@@@@@@@@@@ fix zeroes */
  694. #endif
  695.       }
  696.     } /* if valid line */
  697.     free(line);
  698.       } /* scan lines */
  699.       free(line);
  700.     } /* if protocol */
  701.   } /* if arg */
  702.     
  703.         
  704. /*    Handle command
  705. */
  706.   if (0==strcmp("GET", line1)) {    /* Get a document     */
  707.  
  708. #ifdef OLD_CODE
  709.     if (arg) StrAllocCopy(original, arg)
  710.     keywords=strchr(arg, '?');
  711.     if (keywords) {
  712.         *keywords++ = 0;        /* Chop keywords off */
  713.         if (!*keywords) keywords = NULL;
  714.         else {
  715.         StrAllocCopy(key, keywords);
  716.         char *p;
  717.         for (p=key; *p; p++) if (*p == '+') *p = ' ';
  718.         /* Plusses to spaces */
  719.         HTUnEscape(keywords);
  720.         }
  721.     }
  722. #endif    
  723.     status =  HTRetrieve(arg, soc);
  724.     free(line1);
  725.     return 0;        /* EOF -- please close socket */
  726.   }
  727.  
  728.   HTWriteASCII(soc, "599 Unrecognised method name: `");
  729.   HTWriteASCII(soc, line1);
  730.   HTWriteASCII(soc, "'.\r\n");
  731.     
  732.   if (TRACE) fprintf(stderr,
  733.         "HTDaemon: Unrecognised method `%s'\n", command);
  734.   free(line1);
  735.   return 0;        /* End of file - please close socket */
  736.  
  737.  
  738. } /* handle */
  739.     
  740.  
  741. /*      Handle incoming messages                server_loop()
  742. **    -------------------------
  743. **
  744. ** On entry:
  745. **
  746. **      timeout         -1 for infinite, 0 for poll, else in units of 10ms
  747. **
  748. ** On exit,
  749. **    returns        The status of the operation, <0 if failed.
  750. **            0    means end of file
  751. **
  752. */
  753. #ifdef __STDC__
  754. PRIVATE int server_loop(void)
  755. #else
  756. PRIVATE int server_loop()
  757. #endif
  758. {
  759.     int tcp_status;        /* <0 if error, in general */
  760.     int timeout = -1;        /* No timeout required but code exists */
  761.     for(;;) {
  762.  
  763. /*  If it's a master socket, then find a slave:
  764. */
  765.         if (role == master) {
  766. #ifdef SELECT
  767.         fd_set        read_chans;
  768.         fd_set        write_chans;
  769.         fd_set        except_chans;
  770.         int            nfound;        /* Number of ready channels */
  771.         struct timeval    max_wait;   /* timeout in form for select() */
  772.     
  773.         FD_ZERO(&write_chans);        /* Clear the write mask */
  774.         FD_ZERO(&except_chans);        /* Clear the exception mask */
  775.  
  776. /*  If timeout is required, the timeout structure is set up. Otherwise
  777. **  (timeout<0) a zero is passed instead of a pointer to the struct timeval.
  778. */
  779.         if (timeout>=0) {
  780.         max_wait.tv_sec = timeout/100;
  781.         max_wait.tv_usec = (timeout%100)*10000;
  782.         }
  783.     
  784.         for (com_soc=(-1); com_soc<0;) {    /* Loop while connections keep coming */
  785.     
  786.         
  787. /*  The read mask expresses interest in the master channel for incoming
  788. **  connections) or any slave channel (for incoming messages).
  789. */
  790.  
  791. /*  Wait for incoming connection or message
  792. */
  793.             read_chans = open_sockets;     /* Read on all active channels */
  794.         if (TRACE) printf(
  795. "Daemon: Waiting for connection or message. (Mask=%x hex, max=%x hex).\n", 
  796.              *(unsigned int *)(&read_chans),
  797.             (unsigned int)num_sockets);
  798.         nfound=select(num_sockets, &read_chans,
  799.             &write_chans, &except_chans,
  800.             timeout >= 0 ? &max_wait : 0);
  801.     
  802.         if (nfound<0) return HTInetStatus("select()");
  803.         if (nfound==0) return 0;    /* Timeout */
  804.  
  805. /*    We give priority to existing connected customers. When there are
  806. **    no outstanding commands from them, we look for new customers.
  807. */
  808. /*      If a message has arrived on one of the channels, take that channel:
  809. */
  810.         {
  811.             int i;
  812.             for(i=0; i<num_sockets; i++)
  813.             if (i != master_soc)
  814.                 if (FD_ISSET(i, &read_chans)) {
  815.                 if (TRACE) printf(
  816.                     "Message waiting on socket %d\n", i);
  817.                 com_soc = i;        /* Got one! */
  818.                 break;
  819.             }
  820.             if (com_soc>=0) break; /* Found input socket */
  821.             
  822.         } /* block */
  823.         
  824. /*  If an incoming connection has arrived, accept the new socket:
  825. */
  826.         if (FD_ISSET(master_soc, &read_chans)) {
  827.                 soc_addrlen = sizeof(soc_address); /* JS 930107 */
  828.             CTRACE(tfp, "Daemon: New incoming connection:\n");
  829.             tcp_status = accept(master_soc,
  830.                     (struct sockaddr *)&soc_address,
  831.                     &soc_addrlen);
  832.             if (tcp_status<0)
  833.                 return HTInetStatus("accept");
  834.             CTRACE(tfp, "Daemon: Accepted new socket %d\n",
  835.                 tcp_status);
  836.             FD_SET(tcp_status, &open_sockets);
  837.             if ((tcp_status+1) > num_sockets)
  838.                 num_sockets=tcp_status+1;
  839.     
  840.         } /* end if new connection */
  841.     
  842.     
  843.         } /* loop on event */
  844.     
  845. #else    /* SELECT not supported */
  846.     
  847. /*        if (com_soc<0)   No slaves: must accept */
  848.           SockA peer_soc;
  849.           int peer_len = sizeof (peer_soc);
  850.             CTRACE(tfp, 
  851.             "Daemon: Waiting for incoming connection...\n");
  852. #ifdef DECNET
  853.             tcp_status = accept(master_soc, &peer_soc, &peer_len);
  854. #else  /* For which machine is this ??? rsoc is undeclared, what's mdp ? */
  855.             tcp_status = accept(master_soc,
  856.                     &rsoc->mdp.soc_tcp.soc_address,
  857.                     &rsoc->mdp.soc_tcp.soc_addrlen);
  858. #endif
  859.             if (tcp_status<0)
  860.             return HTInetStatus("accept");
  861.             com_soc = tcp_status;    /* socket number */
  862.             CTRACE(tfp, "Daemon: Accepted socket %d\n", tcp_status);
  863. /*        }  end if no slaves */
  864.     
  865. #endif
  866.  
  867.     }  /* end if master */
  868.  
  869.  
  870. /* com_soc is now valid for read */
  871.  
  872.     {
  873.         SockA addr;
  874.         int namelen = sizeof(addr);
  875.         char ip_address[16];
  876. #ifdef DECNET
  877.         StrAllocCopy(HTClientHost, "DecnetClient");
  878.         /* TBD */
  879. #else
  880.         getpeername(com_soc, (struct sockaddr*)&addr, &namelen);
  881.         
  882.         strncpy(ip_address,
  883.              inet_ntoa(addr.sin_addr), sizeof(ip_address));
  884.         StrAllocCopy(HTClientHost, ip_address);
  885. #endif
  886.     }
  887.  
  888. /*  Read the message now on whatever channel there is:
  889. */
  890.         CTRACE(tfp,"Daemon: Reading socket %d from host %s\n",
  891.         com_soc, HTClientHost);
  892.  
  893.     tcp_status=HTHandle(com_soc);
  894.     
  895.     if(tcp_status<=0) {                /* EOF or error */
  896.         if (tcp_status<0) {                /* error */
  897.             CTRACE(tfp,
  898.         "Daemon: Error %d handling incoming message (errno=%d).\n",
  899.              tcp_status, errno);
  900.             /* DONT return HTInetStatus("netread");     error */
  901.         } else {
  902.         CTRACE(tfp, "Daemon: Socket %d disconnected by peer\n",
  903.             com_soc);
  904.             }
  905.         if (role==master) {
  906.         NETCLOSE(com_soc);
  907. #ifdef SELECT
  908.         FD_CLR(com_soc, &open_sockets);
  909. #endif
  910.         } else {  /* Not multiclient mode */
  911. #ifdef VM
  912.         return -69;
  913. #else
  914.         return -ECONNRESET;
  915. #endif
  916.         }
  917.     } else {/* end if handler left socket open */
  918.         NETCLOSE(com_soc);
  919. #ifdef SELECT
  920.         FD_CLR(com_soc, &open_sockets);
  921. #endif
  922.         }
  923.     }; /* for loop */
  924. /*NOTREACHED*/
  925. } /* end server_loop */
  926.  
  927.  
  928. /*        Main program
  929. **        ------------
  930. **
  931. **    Options:
  932. **    -v        verify: turn trace output on to stdout 
  933. **    -a addr        Use different tcp port number and style
  934. **    -p port        Prefered
  935. **    -l file        Log requests in ths file
  936. **    -r file        Take rules from this file
  937. **    -R file        Clear rules and take rules from file.
  938. **
  939. **    Parameters:
  940. **        directory    directory to export
  941. */
  942. int main ARGS2 (
  943.     int,    argc,
  944.     char**,    argv)
  945. {
  946.     int status;
  947. #ifdef RULES
  948.     int rulefiles = 0;        /* Count number loaded */
  949. #endif
  950.     char * addr = "";        /* default address */
  951.     char *directory = NULL;
  952.  
  953.     WWW_TraceFlag = 0;        /* diagnostics off by default */
  954.  
  955.     if (TRACE) fprintf(stderr,
  956.         "HTDaemon: This is %s, version %s, using libwww version %s\n",
  957.         HTAppName, HTAppVersion, HTLibraryVersion);
  958.  
  959. #ifdef RULES
  960.     HTClearRules();
  961. #endif
  962.  
  963.     {
  964.         int a;
  965.         
  966.     for (a=1; a<argc; a++) {
  967.         
  968.         if (0==strcmp(argv[a], "-v")) {
  969.             WWW_TraceFlag = 1;
  970.  
  971.         } else if (0==strcmp(argv[a], "-version")) {
  972.             printf("CERN World-Wide Web Daemon %s, libwww %s\n",
  973.             VD, HTLibraryVersion);
  974.         exit(0);
  975.  
  976.         } else if (0==strcmp(argv[a], "-a")) {
  977.             if (++a<argc) addr = argv[a];
  978.  
  979.         } else if (0==strcmp(argv[a], "-p")) {
  980.             if (++a<argc) {
  981.             addr = (char*)malloc(strlen(argv[a])+10);
  982.             sprintf(addr, "*:%s", argv[a]);
  983.         }
  984. #ifdef RULES
  985.         } else if (0==strcmp(argv[a], "-r")) {
  986.             if (++a<argc) { 
  987.             if (HTLoadRules(argv[a]) < 0) exit(-1);
  988.             rulefiles++;
  989.         }
  990.         } else if (0==strcmp(argv[a], "-R")) {
  991.         rulefiles++;        /* Inhibit rule file load */
  992. #endif
  993. #ifdef DIR_OPTIONS
  994.         } else if (0==strncmp(argv[a], "-d", 2)) {
  995.             char *p = argv[a]+2;
  996.         for(;*p;p++) {
  997.             switch (argv[a][2]) {
  998.             case 'b':    HTDirReadme = HT_DIR_README_BOTTOM; break;
  999.             case 'n':    HTDirAccess = HT_DIR_FORBID; break;
  1000.             case 'r':    HTDirReadme = HT_DIR_README_NONE; break;
  1001.             case 's':   HTDirAccess = HT_DIR_SELECTIVE; break;
  1002.             case 't':    HTDirReadme = HT_DIR_README_TOP; break;
  1003.             case 'y':    HTDirAccess = HT_DIR_OK; break;
  1004.             default:
  1005.             fprintf(stderr, 
  1006.                "HTDaemon: bad -d option %s\n", argv[a]);
  1007.             exit(-4);
  1008.             }
  1009.         } /* loop over characters */
  1010. #endif
  1011.  
  1012.         } else if (0==strcmp(argv[a], "-l")) { /* template */
  1013.             if (++a<argc) {
  1014.             time(&theTime);
  1015.             gmt = gmtime(&theTime);
  1016.             log_file_name = malloc(strlen(argv[a]) + 5 + 1);
  1017.             sprintf(log_file_name,
  1018.                 argv[a], (gmt->tm_year) %100 , gmt->tm_mon + 1);
  1019.             serverlog = fopen(log_file_name, "a");
  1020.         }
  1021.         if (!serverlog) {
  1022.             fprintf(stderr,
  1023.             "Can't open log file %s\n", argv[a]);
  1024.             serverlog = stderr;
  1025.         }
  1026.         } else if (argv[a][0] != '-') {    /* Parameter */
  1027.             if (!directory) directory = argv[a];
  1028.         
  1029.         } /*ifs */
  1030.     } /* for each arg */
  1031.     } /* scope of a */
  1032.  
  1033. #ifdef RULES
  1034.  
  1035.     if (rulefiles==0) {        /* No mention */
  1036.         if (!directory) {
  1037.         if (HTLoadRules(RULE_FILE) < 0) {    /* Default rule file? */
  1038.             directory = DEFAULT_EXPORT;
  1039.         };
  1040.     }
  1041.     if (directory) {
  1042.         char * mapto = malloc(strlen(directory)+5);
  1043.         sprintf(mapto, "file://%s%s/*", HTHostName(), directory);
  1044.         HTAddRule(HT_Pass, "/*", mapto);
  1045.         HTAddRule(HT_Fail, "*", NULL);
  1046.     }
  1047.     } else {
  1048.         if (directory) {
  1049.         fprintf(stderr,
  1050.             "Warning: -r or -R specified so %s directory param ignored\n",
  1051.             directory);
  1052.     }
  1053.     }
  1054. #endif
  1055.     
  1056.     
  1057.     status = do_bind(addr);
  1058.     if (status<0) {
  1059.         fprintf(stderr, "Daemon: Bad setup: Can't bind and listen on port.\n");
  1060.         fprintf(stderr, "     (Possibly server already running, for example).\n");
  1061.     exit(status);
  1062.     }
  1063.     
  1064.     status = server_loop();
  1065.  
  1066.     if (status<0) {
  1067.         /* printf("Error in server loop.\n");  not error if inetd-started*/
  1068.     exit(status);
  1069.     }
  1070.     
  1071.     exit(0);
  1072.     return 0;    /* NOTREACHED -- For gcc */
  1073. }
  1074.  
  1075.